﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Drawing;
using Windows.Win32.System.Variant;
using Windows.Win32.UI.Accessibility;

namespace System.Windows.Forms;

public partial class DataGridViewRowHeaderCell
{
    protected class DataGridViewRowHeaderCellAccessibleObject : DataGridViewCellAccessibleObject
    {
        public DataGridViewRowHeaderCellAccessibleObject(DataGridViewRowHeaderCell? owner) : base(owner)
        {
        }

        public override Rectangle Bounds
        {
            get
            {
                if (Owner is null)
                {
                    throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
                }

                if (Owner.DataGridView is null || Owner.OwningRow is null || ParentPrivate is null)
                {
                    return Rectangle.Empty;
                }

                // use the parent row acc obj bounds
                Rectangle rowRect = ParentPrivate.Bounds;
                Rectangle cellRect = rowRect;
                cellRect.Width = Owner.DataGridView.RowHeadersWidth;
                if (Owner.DataGridView.RightToLeft == RightToLeft.Yes)
                {
                    cellRect.X = rowRect.Right - cellRect.Width;
                }

                return cellRect;
            }
        }

        public override string DefaultAction
        {
            get
            {
                if (Owner is null)
                {
                    throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
                }

                if (Owner.DataGridView is not null &&
                    (Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect ||
                     Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect))
                {
                    return SR.DataGridView_RowHeaderCellAccDefaultAction;
                }

                return string.Empty;
            }
        }

        public override string? Name => ParentPrivate?.Name ?? string.Empty;

        public override AccessibleObject? Parent => ParentPrivate;

        private AccessibleObject? ParentPrivate
        {
            get
            {
                if (Owner is null)
                {
                    throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
                }

                return Owner.OwningRow?.AccessibilityObject;
            }
        }

        public override AccessibleRole Role => AccessibleRole.RowHeader;

        public override AccessibleStates State
        {
            get
            {
                if (Owner is null)
                {
                    throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
                }

                AccessibleStates resultState = AccessibleStates.Selectable;

                // get the Offscreen state from the base method.
                AccessibleStates state = base.State;
                if ((state & AccessibleStates.Offscreen) == AccessibleStates.Offscreen)
                {
                    resultState |= AccessibleStates.Offscreen;
                }

                if (Owner.DataGridView is not null &&
                    (Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect ||
                     Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect))
                {
                    if (Owner.OwningRow is not null && Owner.OwningRow.Selected)
                    {
                        resultState |= AccessibleStates.Selected;
                    }
                }

                return resultState;
            }
        }

        public override string Value => string.Empty;

        public override void DoDefaultAction()
        {
            if (Owner is null)
            {
                throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
            }

            if (Owner.DataGridView?.IsHandleCreated == true &&
                Owner.OwningRow is not null &&
                (Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect ||
                 Owner.DataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect))
            {
                Owner.OwningRow.Selected = true;
            }
        }

        public override AccessibleObject? Navigate(AccessibleNavigation navigationDirection)
        {
            if (Owner is null)
            {
                throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
            }

            if (Owner.OwningRow is null || Owner.DataGridView is null)
            {
                return null;
            }

            switch (navigationDirection)
            {
                case AccessibleNavigation.Next:
                    return (Owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) > 0)
                        ? ParentPrivate?.GetChild(1) // go to the next sibling
                        : null;

                case AccessibleNavigation.Down:
                    {
                        if (Owner.OwningRow.Index == Owner.DataGridView.Rows.GetLastRow(DataGridViewElementStates.Visible))
                        {
                            return null;
                        }

                        int nextVisibleRow = Owner.DataGridView.Rows.GetNextRow(Owner.OwningRow.Index, DataGridViewElementStates.Visible);
                        int actualDisplayIndex = Owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, nextVisibleRow);

                        if (Owner.DataGridView.ColumnHeadersVisible)
                        {
                            // Add 1 because the first child in the data grid view acc obj is the top row header acc obj
                            actualDisplayIndex++;
                        }

                        return Owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex)?.GetChild(0);
                    }

                case AccessibleNavigation.Up:
                    {
                        if (Owner.OwningRow.Index == Owner.DataGridView.Rows.GetFirstRow(DataGridViewElementStates.Visible))
                        {
                            if (!Owner.DataGridView.ColumnHeadersVisible)
                            {
                                return null;
                            }

                            // Return the top left header cell accessible object.
                            Debug.Assert(Owner.DataGridView.TopLeftHeaderCell.AccessibilityObject == Owner.DataGridView.AccessibilityObject.GetChild(0)!.GetChild(0));
                            return Owner.DataGridView.AccessibilityObject.GetChild(0)?.GetChild(0);
                        }
                        else
                        {
                            int previousVisibleRow = Owner.DataGridView.Rows.GetPreviousRow(Owner.OwningRow.Index, DataGridViewElementStates.Visible);
                            int actualDisplayIndex = Owner.DataGridView.Rows.GetRowCount(DataGridViewElementStates.Visible, 0, previousVisibleRow);
                            if (Owner.DataGridView.ColumnHeadersVisible)
                            {
                                // Add 1 because the first child in the data grid view acc obj is the top row header acc obj
                                actualDisplayIndex++;
                            }

                            return Owner.DataGridView.AccessibilityObject.GetChild(actualDisplayIndex)?.GetChild(0);
                        }
                    }

                case AccessibleNavigation.Previous:
                default:
                    return null;
            }
        }

        public override void Select(AccessibleSelection flags)
        {
            if (Owner is null)
            {
                throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
            }

            if (Owner is not DataGridViewRowHeaderCell dataGridViewCell)
            {
                return;
            }

            DataGridView? dataGridView = dataGridViewCell.DataGridView;
            if (dataGridView?.IsHandleCreated != true)
            {
                return;
            }

            if ((flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus)
            {
                dataGridView.Focus();
            }

            if (dataGridViewCell.OwningRow is not null &&
                (dataGridView.SelectionMode == DataGridViewSelectionMode.FullRowSelect ||
                 dataGridView.SelectionMode == DataGridViewSelectionMode.RowHeaderSelect))
            {
                if ((flags & (AccessibleSelection.TakeSelection | AccessibleSelection.AddSelection)) != 0)
                {
                    dataGridViewCell.OwningRow.Selected = true;
                }
                else if ((flags & AccessibleSelection.RemoveSelection) == AccessibleSelection.RemoveSelection)
                {
                    dataGridViewCell.OwningRow.Selected = false;
                }
            }
        }

        #region IRawElementProviderFragment Implementation

        internal override IRawElementProviderFragment.Interface? FragmentNavigate(NavigateDirection direction)
        {
            if (Owner is null)
            {
                throw new InvalidOperationException(SR.DataGridViewCellAccessibleObject_OwnerNotSet);
            }

            if (Owner.OwningRow is null)
            {
                return null;
            }

            return direction switch
            {
                NavigateDirection.NavigateDirection_Parent => Owner.OwningRow.AccessibilityObject,
                NavigateDirection.NavigateDirection_NextSibling =>
                        (Owner.DataGridView is not null && Owner.DataGridView.Columns.GetColumnCount(DataGridViewElementStates.Visible) > 0)
                            ? Owner.OwningRow.AccessibilityObject.GetChild(1) // go to the next sibling
                            : null,
                _ => null,
            };
        }

        #endregion

        #region IRawElementProviderSimple Implementation

        internal override VARIANT GetPropertyValue(UIA_PROPERTY_ID propertyId)
            => propertyId switch
            {
                UIA_PROPERTY_ID.UIA_ControlTypePropertyId => (VARIANT)(int)UIA_CONTROLTYPE_ID.UIA_HeaderControlTypeId,
                UIA_PROPERTY_ID.UIA_HasKeyboardFocusPropertyId => VARIANT.False,
                UIA_PROPERTY_ID.UIA_IsEnabledPropertyId => (VARIANT)(Owner?.DataGridView?.Enabled ?? false),
                UIA_PROPERTY_ID.UIA_IsKeyboardFocusablePropertyId => (VARIANT)State.HasFlag(AccessibleStates.Focusable),
                _ => base.GetPropertyValue(propertyId),
            };

        #endregion
    }
}
